#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "hi_wifi_api.h"
#include "lwip/ip_addr.h"
#include "lwip/netifapi.h"
#include "lwip/sockets.h"
#include "MQTTClient.h"
// 新增：DHT11温湿度采集所需头文件与宏定义
#include "hi_gpio.h"
#include "hi_io.h"
#include "hi_time.h"
#include <MQTTConnect.h>

#define DHT11_GPIO         7               // DHT11数据引脚（与dht11_example.c保持一致）
#define u8 unsigned char
#define u16 unsigned short
#define u32 unsigned int

#define DHT11_DQ_OUT_HIGH  hi_gpio_set_ouput_val(DHT11_GPIO, HI_GPIO_VALUE1)
#define DHT11_DQ_OUT_LOW   hi_gpio_set_ouput_val(DHT11_GPIO, HI_GPIO_VALUE0)

// MQTT原有全局变量
static MQTTClient mq_client;
unsigned char *onenet_mqtt_buf;
unsigned char *onenet_mqtt_readbuf;
int buf_size;
Network n;
MQTTPacket_connectData data = MQTTPacket_connectData_initializer; 
// 新增：温湿度存储变量
static u8 temperature = 0;
static u8 humidity = 0;
static hi_gpio_value DHT11_DQ_IN;         // 存储DHT11输入电平


// 新增：DHT11底层驱动函数（直接复用dht11_example.c逻辑）
static u8 GPIOGETINPUT(hi_io_name id, hi_gpio_value *val)
{
    hi_gpio_get_input_val(id, val);
    return *val;
}

static void DHT11_IO_OUT(void)
{
    hi_gpio_set_dir(DHT11_GPIO, HI_GPIO_DIR_OUT);
}

static void DHT11_IO_IN(void)
{
    hi_gpio_set_dir(DHT11_GPIO, HI_GPIO_DIR_IN);
    hi_io_set_pull(DHT11_GPIO, HI_IO_PULL_NONE);
}

static void DHT11_Rst(void)
{
    DHT11_IO_OUT();          // 切换为输出模式
    DHT11_DQ_OUT_LOW;        // 拉低总线至少18ms
    hi_udelay(20000);        // 延时20ms（满足时序要求）
    DHT11_DQ_OUT_HIGH;       // 拉高总线20-40us
    hi_udelay(35);           // 延时35us（取中间值）
}

static u8 DHT11_Check(void)
{
    u8 retry = 0;
    DHT11_IO_IN();           // 切换为输入模式
    // 等待DHT11拉低总线（40-80us）
    while (GPIOGETINPUT(DHT11_GPIO, &DHT11_DQ_IN) && retry < 100)
    {
        retry++;
        hi_udelay(1);
    }
    if (retry >= 100) return 1;  // 超时无响应
    else retry = 0;
    // 等待DHT11拉高总线（40-80us）
    while ((!GPIOGETINPUT(DHT11_GPIO, &DHT11_DQ_IN)) && retry < 100)
    {
        retry++;
        hi_udelay(1);
    }
    if (retry >= 100) return 1;  // 超时无响应
    return 0;                   // 响应正常
}

static u8 DHT11_Init(void)
{
    hi_io_set_func(DHT11_GPIO, HI_IO_FUNC_GPIO_7_GPIO);
    hi_gpio_set_dir(DHT11_GPIO, HI_GPIO_DIR_OUT);
    hi_gpio_set_ouput_val(DHT11_GPIO, HI_GPIO_VALUE1);
    DHT11_Rst();
    return DHT11_Check();
}

// 从DHT11读取一个位
static u8 DHT11_Read_Bit(void)
{
    u8 retry = 0;
    while (GPIOGETINPUT(DHT11_GPIO, &DHT11_DQ_IN) && retry < 100)//等待变为低电平
    {
        retry++;
        hi_udelay(1);
    }
    retry = 0;
    while ((!GPIOGETINPUT(DHT11_GPIO, &DHT11_DQ_IN)) && retry < 100)//等待变高电平
    {
        retry++;
        hi_udelay(1);
    }
    hi_udelay(40);//等待40us判断高低电平（数据1或0）
    if (GPIOGETINPUT(DHT11_GPIO, &DHT11_DQ_IN)) return 1;
    else return 0;
}

// 从DHT11读取一个字节
static u8 DHT11_Read_Byte(void)
{
    u8 i, dat;
    dat = 0;
    for (i = 0; i < 8; i++)
    {
        dat <<= 1;
        dat |= DHT11_Read_Bit();
    }
    return dat;
}

// 从DHT11读取一次温湿度数据
static u8 DHT11_Read_Data(u8 *temp, u8 *humi)
{
    u8 buf[5] = {0};
    u8 i;
    DHT11_Rst();
    if (DHT11_Check() == 0)
    {
        for (i = 0; i < 5; i++)//读取40位数据
        {
            buf[i] = DHT11_Read_Byte();
        }
        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])//数据校验
        {
            *humi = buf[0];
            *temp = buf[2];
        }
    }
    else return 1;
    return 0;
}

// 新增：DHT11_DQ_OUT_HIGH/LOW 宏定义（补充驱动依赖）
#define DHT11_DQ_OUT_HIGH  hi_gpio_set_ouput_val(DHT11_GPIO, HI_GPIO_VALUE1)
#define DHT11_DQ_OUT_LOW   hi_gpio_set_ouput_val(DHT11_GPIO, HI_GPIO_VALUE0)

// MQTT原有消息回调函数
void mqtt_callback(MessageData *msg_data)
{
    size_t res_len = 0;
    uint8_t *response_buf = NULL;
    char topicname[45] = { "$crsp/" };
    LOS_ASSERT(msg_data);
    printf("topic %.*s receive a message\r\n", msg_data->topicName->lenstring.len, msg_data->topicName->lenstring.data);
    printf("message is %.*s\r\n", msg_data->message->payloadlen, msg_data->message->payload);
}

// MQTT连接与数据发布函数（核心修改：添加DHT11初始化与温湿度上传）
int mqtt_connect(void)
{
    int rc = 0;
    NetworkInit(&n);
    // 连接MQTT服务器（免费测试服务器EMQ）
    NetworkConnect(&n, "broker.emqx.io", 1883);

    // 分配MQTT缓冲区内存
    buf_size = 4096 + 1024;
    onenet_mqtt_buf = (unsigned char *) malloc(buf_size);
    onenet_mqtt_readbuf = (unsigned char *) malloc(buf_size);
    if (!(onenet_mqtt_buf && onenet_mqtt_readbuf))
    {
        printf("No memory for MQTT client buffer!");
        return -2;
    }

    // 初始化MQTT客户端
    MQTTClientInit(&mq_client, &n, 1000, onenet_mqtt_buf, buf_size, onenet_mqtt_readbuf, buf_size);
    MQTTStartTask(&mq_client);

    // 配置MQTT连接参数
    data.keepAliveInterval = 30;
    data.cleansession = 1;
    // 客户端ID、用户名、密码（根据实际需求修改）
    data.clientID.cstring = "team12";
    data.username.cstring = "team12";
    data.password.cstring = "123456";
    mq_client.defaultMessageHandler = mqtt_callback;

    // 新增：初始化DHT11温湿度模块
    hi_gpio_init();//初始化GPIO外设
    while (DHT11_Init()) //等待DHT11初始化成功
    {
        printf("DHT11 Init Error!!\r\n");
        usleep(100000); //初始化失败时，100ms后重试
    }
    printf("DHT11 Init Successful!!\r\n");

    // 连接MQTT服务器
    rc = MQTTConnect(&mq_client, &data);
    // MQTT订阅主题（可根据需求修改主题名）
    char topicname[] = "expand:mqtt02-team12-dht11-01";
    MQTTSubscribe(&mq_client, topicname, 0, mqtt_callback);

    // 循环：读取温湿度并上传到MQTT服务器
    while (1)
    {
        MQTTMessage message;
        message.qos = 0;
        message.retained = 0;
        char payload[64] = {0};//存储格式化后的温湿度数据

        // 读取DHT11温湿度数据
        if (DHT11_Read_Data(&temperature, &humidity) == 0)
        {
            // 格式化数据（示例："Temp:25°C, Hum:60%"，可自定义格式）
            snprintf(payload, sizeof(payload), "Temp:%d°C, Hum:%d%%", temperature, humidity);
            printf("Upload MQTT Data: %s\r\n", payload);
        }
        else
        {
            // 读取失败时，发送错误提示
            strncpy(payload, "DHT11 Read Data Failed", sizeof(payload)-1);
            printf("DHT11 Read Error, Send Warn Message\r\n");
        }

        // 配置MQTT消息 payload
        message.payload = (void *)payload;
        message.payloadlen = strlen(payload);

        // 发布消息到MQTT服务器
        if (MQTTPublish(&mq_client, topicname, &message) < 0)
        {
            printf("MQTTPublish failed !\r\n");
        }

        usleep(5000000); // 5秒上传一次（避免数据过于频繁，可调整间隔）
    }
    return 0;
}

// MQTT测试入口函数（原有逻辑不变）
void mqtt_test(void)
{
    mqtt_connect();
}